<?php

namespace eValor\BiliOpenLiveSdk;

use Curl\Curl;
use eValor\BiliOpenLiveSdk\Exceptions\GatewayException;
use eValor\BiliOpenLiveSdk\Utils\Helper;
use eValor\BiliOpenLiveSdk\Utils\SdkLog;
use Throwable;

/**
 * 基础服务
 * Class BasicService
 * @package eValor\BiliOpenLiveSdk
 */
abstract class BasicService
{
    /**
     * 服务配置
     * @var array
     */
    protected array $config;

    /**
     * 日志管理类
     * @var SdkLog
     */
    protected SdkLog $logger;

    /**
     * 生产环境域名
     * @var string
     */
    protected string $prodDomain = 'https://live-open.biliapi.com';

    /**
     * 构造函数
     * @param $config
     */
    public function __construct($config)
    {
        $this->config = $config;
        $this->logger = new SdkLog($this->config['logPath'], $this->config['logRecycleDay'], $this->config['logRecycleRate']);
    }

    /**
     * 发起Post请求
     * @param string $url
     * @param array|string $reqParams
     * @param array $headers
     * @return mixed
     * @throws GatewayException
     * @throws Throwable
     */
    protected function httpPost(string $url, $reqParams = [], array $headers = [])
    {
        $url = $this->buildRequestUrl($url);

        try {
            // 初始化客户端并处理请求头
            $client = $this->initHttpClient();

            if ($headers) {
                foreach ($headers as $name => $value) {
                    $client->setHeader($name, $value);
                }
            }

            // 发起POST请求(如果需要作为JSON请提前在外部处理)
            $curlResponse = $client->post($url, $reqParams, true);

            // 处理响应结果
            if (!$curlResponse->isSuccess()) {
                $httpStatus = $curlResponse->getHttpStatus();
                throw new GatewayException('Abnormal response status: ' . $httpStatus);
            }

            // 返回的内容为空
            if (empty($curlResponse->getResponse())) {
                throw new GatewayException('Abnormal response: response body is empty.');
            }

            // 获取返回内容
            $responseData = Helper::jsonDecode($curlResponse->getResponse());

            // 返回存在错误
            if (isset($responseData['code']) && $responseData['code'] != 0) {
                throw new GatewayException('Abnormal response: response error => ' . $responseData['message'] ?? 'UNKNOWN', $responseData['code']);
            }

            // 返回完整的响应内容
            $this->logger->logCurl($url, $reqParams, $curlResponse);
            return $responseData;

        } catch (Throwable $throwable) {

            // 记录日志并抛出异常
            $this->logger->logCurl($url, $reqParams, $curlResponse ?? null, $throwable);
            throw $throwable;

        }
    }

    /**
     * 发起签名请求
     * @param string $url
     * @param array $reqParams
     * @return mixed
     * @throws GatewayException
     * @throws Throwable
     */
    protected function httpPostWithSign(string $url, array $reqParams = [])
    {
        // 打包请求内容为Json
        $JsonContent = Helper::jsonEncode($reqParams);

        // 构建Bilibili公共请求头
        $commonHeader = [
            'x-bili-content-md5'       => md5($JsonContent),
            'x-bili-timestamp'         => time(),
            'x-bili-signature-method'  => 'HMAC-SHA256',
            'x-bili-signature-nonce'   => substr(md5(random_bytes(32)), 8, 16),
            'x-bili-accesskeyid'       => $this->config['accessKey'],
            'x-bili-signature-version' => '1.0'
        ];

        // 为公共请求头生成签名
        $signStr = '';
        ksort($commonHeader);
        foreach ($commonHeader as $name => $value) $signStr .= "$name:$value\n";
        $signStr = rtrim($signStr, "\n");
        $headerAuthorization = hash_hmac("sha256", $signStr, $this->config['secretKey']);

        // 合并请求头
        $headers = array_merge($commonHeader, [
            'Accept'         => 'application/json',
            'Content-Type'   => 'application/json',
            'Content-Length' => strlen($JsonContent),
            'Authorization'  => $headerAuthorization
        ]);

        // 请求并返回响应
        return $this->httpPost($url, $reqParams, $headers);
    }

    /**
     * 初始化请求客户端
     * @return Curl
     */
    private function initHttpClient(): Curl
    {
        $curl = new Curl();
        $curl->setOpt(CURLOPT_SSL_VERIFYPEER, false); // 不校验证书
        $curl->setOpt(CURLOPT_SSL_VERIFYHOST, false); // 不校验域名
        $curl->setOpt(CURLOPT_TIMEOUT, $this->config['httpTimeout']); // 超时设定
        $curl->setUserAgent('BiliOpenLiveSdk/' . BiLiveSdk::VERSION);
        $curl->setHeader('Content-Type', 'application/json;charset=utf-8');

        return $curl;
    }

    /**
     * 构建请求路径
     * @param string $url
     * @return string
     */
    private function buildRequestUrl(string $url = ''): string
    {
        return rtrim($this->prodDomain, '/') . '/' . ltrim($url, '/');
    }
}
